package cn.luke.basic.log_api;

import cn.dev33.satoken.stp.StpUtil;
import cn.luke.basic.entity.DtoApiLog;
import cn.luke.basic.enums.EnumBaseParams;
import cn.luke.basic.mapper.MapperApiLog;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * Created by Luke on 2025/3/10 11:16.
 */
@Aspect
@Order(2)
@Component
public class LogApiAspect {

    @Resource
    private MapperApiLog mapperApiLog;
    @Resource
    private ObjectMapper objectMapper; // JSON序列化工具

    @Around("@annotation(logApi)")
    public Object around(ProceedingJoinPoint joinPoint, LogApi logApi) throws Throwable {
        DtoApiLog apiLog = buildApiLog(joinPoint, logApi);

        long startTime = System.currentTimeMillis();
        int hasException = 0;
        String exceptionMsg = null;
        Object result;
        try {
            result = joinPoint.proceed();
            return result;
        } catch (Throwable e) {
            hasException = 1;
            exceptionMsg = e.getMessage();
            throw e;
        } finally {
            apiLog.setDuration(System.currentTimeMillis() - startTime);
            apiLog.setHasException(hasException);
            apiLog.setExceptionMsg(exceptionMsg);
            System.out.println(">>>>>>>>logApi.async():"+logApi.async());
            // 根据配置决定是否异步保存
            if (logApi.async()) {
                CompletableFuture.runAsync(() -> saveLog(apiLog));
            } else {
                saveLog(apiLog);
            }
        }
    }

    // 获取客户端IP（处理代理）
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    private DtoApiLog buildApiLog(ProceedingJoinPoint joinPoint, LogApi logApi) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getRequest();

        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        DtoApiLog apiLog = new DtoApiLog();

        // 基础信息
        String douser;
        try {
            douser = (String) StpUtil.getSession().get(EnumBaseParams.douser.name());
        } catch (Exception ignored) {
            douser = "guest";
        }
        apiLog.setOperation(logApi.value().isEmpty() ? method.getName() : logApi.value());
        apiLog.setMethod(method.getDeclaringClass().getName() + "#" + method.getName());
        apiLog.setIp(getClientIp(request));
        apiLog.setDouser(douser);

        // 参数处理
        Object[] args = joinPoint.getArgs();
        try {
            Map<String, Object> paramMap = new HashMap<>();
            String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
            List<String> excludeParams;
            try {
                excludeParams =Arrays.asList(logApi.excludeParams());
            }catch (Exception ignored){
                excludeParams = new ArrayList<>();
            }
            for (int i = 0; i < args.length; i++) {
                String paramName = paramNames[i];
                Object argValue = args[i];
                // 1. 排除参数名在 excludeParams 中的参数
                if (excludeParams.contains(paramName)) continue;
                // 2. 排除 HttpServletRequest 类型
                if (argValue instanceof HttpServletRequest) continue;
                // 3. 深度过滤 Map 类型参数
                if (argValue instanceof Map<?, ?> originalMap) {
                    Map<Object, Object> filteredMap = new HashMap<>();
                    // 遍历 Map 的键值对
                    for (Map.Entry<?, ?> entry : originalMap.entrySet()) {
                        Object key = entry.getKey();
                        // 如果键的字符串形式在 excludeParams 中，则跳过
                        if (!excludeParams.contains(key.toString())) {
                            filteredMap.put(key, entry.getValue());
                        }
                    }
                    paramMap.put(paramName, filteredMap);
                } else {
                    paramMap.put(paramName, argValue);
                }
            }
            String params = objectMapper.writeValueAsString(paramMap);
            //判断参数长度, 只取前1000
            if (params.length() > 1000) {
                params = params.substring(0, 960);
                params += "...(参数过长,已截断)";
            }
            apiLog.setParams(params);
        } catch (Exception e) {
            apiLog.setParams("参数序列化失败");
        }
        return apiLog;
    }

    /**
     * 使用异步线程保存日志
     */
    @Async("logTaskExecutor")
    public void saveLog(DtoApiLog apiLog) {
        try {
            mapperApiLog.insert(apiLog);
        } catch (Exception e) {
            // 记录日志保存失败信息
            System.err.println("日志保存失败: " + e.getMessage());
        }
    }

}
